/*
 * VariableAst.cpp
 *
 *  Created on: 1 Nov 2010
 *      Author: kn308
 */

#include <iostream>
#include <MAlice/Codegen.h>
#include <MAlice/Ast/VariableAst.h>

#include <MAlice/Ast/StringLiteralAst.h>
#include <MAlice/Ast/IntegerLiteralAst.h>

#include <MAlice/Ast/IfStatementAst.h>
#include <MAlice/Ast/ConditionalExpressionAst.h>
#include <MAlice/Ast/BinaryOrOperatorAst.h>
#include <MAlice/Ast/BinaryLessThanOperatorAst.h>
#include <MAlice/Ast/BinaryGreaterThanEqualOperatorAst.h>
#include <MAlice/Ast/ReturnExpressionAst.h>

#include <MAlice/Ast/PrintAst.h>
#include <MAlice/Parser/location.hh>

namespace MAlice
{

VariableAst::VariableAst(location loc, const string& name) :
	ExpressionAst(loc), mParam(false), mName(name), mOffset(
			new IntegerLiteralAst(mLocation, 0))
{
	MALICE_TRACE;
}

VariableAst::VariableAst(location loc, const string& name,
		ExpressionAst* offset, int position) :
	ExpressionAst(loc), mParam(false), mName(name), mOffset(offset), mPosition(
			position)
{
	MALICE_TRACE;
}

VariableAst::~VariableAst()
{
	MALICE_TRACE;
}

struct Helper: public ExpressionAst
{
	Value* mValue;

	Helper(Value* value) : ExpressionAst(location()),
		mValue(value)
	{
	}

	virtual Value* generate()
	{
		return mValue;
	}
};

Value* VariableAst::getPointer()
{
#	ifndef NDEBUG
	cout << "VariableAst::getPointer()" << endl;
#	endif

	Codegen* g = Codegen::get();

	// get the i-th element of the pointer
	if (Value* value = g->getVar(getName()))
	{
		Value* variableSize = g->getVar("size:" + getName());
		if (variableSize == NULL)
		{
#ifdef DEBUG
			cerr << "Internal Warning: Couldn't get the array size for "
					<< getName() << "!" << endl;
#endif
		} else
		{
			ExpressionAst* mSize = new Helper(variableSize);
			ExpressionAst* condition = new BinaryOrOperatorAst(mLocation,
					new BinaryGreaterThanEqualOperatorAst(mLocation, mOffset, mSize),
					new BinaryLessThanOperatorAst(mLocation,
							mOffset,
							new IntegerLiteralAst(mLocation, 0)));

			ListAst* statement = new ListAst(mLocation);
			statement->adopt(new PrintAst(mLocation, new StringLiteralAst(mLocation, "Oh no! You wanted their ")));
			statement->adopt(new PrintAst(mLocation, mOffset));
			statement->adopt(new PrintAst(mLocation, new StringLiteralAst(mLocation, " piece, but they only had ")));
			statement->adopt(new PrintAst(mLocation, mSize));
			statement->adopt(new PrintAst(mLocation, new StringLiteralAst(mLocation, ". Check paragraph ")));
			statement->adopt(new PrintAst(mLocation, new IntegerLiteralAst(mLocation, mPosition)));
			statement->adopt(new PrintAst(mLocation, new StringLiteralAst(mLocation, " of the story again!")));
			statement->adopt(new ReturnExpressionAst(mLocation, new IntegerLiteralAst(mLocation,1)));

			// check if the array bounds is correct
			IfStatementAst* check = new IfStatementAst(mLocation,
					new ConditionalExpressionAst(mLocation, condition, statement));
			check->generate();
		}

		Value* pointer = g->getBuilder()->CreateGEP(value, mOffset->generate());

		return pointer;
	} else
	{
		throw runtime_error(getName() + " is not defined!");
	}
}

Value* VariableAst::generate()
{
	MALICE_TRACE;
#	ifndef NDEBUG
	cout << "VariableAst::generate()" << endl;
#	endif

	Codegen* g = Codegen::get();
	Builder* builder = g->getBuilder();
	Value * pointer = getPointer();

	return builder->CreateLoad(pointer, mName);
}

}
